// 版权归Go作者所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:build darwin | dragonfly | freebsd | linux | netbsd | openbsd 
// +build darwin dragonfly freebsd linux netbsd openbsd 

package bio

import (
	"runtime"
	"sync/atomic"
	"syscall"
)

// mmapLimit是
// 返回到堆分配的片读取之前创建的最大mmaped区域数。这是因为一些操作系统对每个进程的不同映射区域的数量进行了限制。截至本文撰写时：
// 
// Darwin unlimited 
// DragonFly 1000000（vm.max_proc_mmap）
// FreeBSD unlimited 
// Linux65530（vm.max_map_count）
// NetBSD unlimited 
// OpenBSD unlimited 
var mmapLimit int32 = 1<<31 - 1

func init() {
	// Linux是唯一一款实用的操作系统。
	if runtime.GOOS == "linux" {
		mmapLimit = 30000
	}
}

func (r *Reader) sliceOS(length uint64) ([]byte, bool) {
	// 对于小片段，不要担心
	// 映射的开销，尤其是因为我们无法取消映射。
	const threshold = 16 << 10
	if length < threshold {
		return nil, false
	}

	// 我们达到mmap限制了吗？
	if atomic.AddInt32(&mmapLimit, -1) < 0 {
		atomic.AddInt32(&mmapLimit, 1)
		return nil, false
	}

	// 页面对齐偏移量。
	off := r.Offset()
	align := syscall.Getpagesize()
	aoff := off &^ int64(align-1)

	data, err := syscall.Mmap(int(r.f.Fd()), aoff, int(length+uint64(off-aoff)), syscall.PROT_READ, syscall.MAP_SHARED|syscall.MAP_FILE)
	if err != nil {
		return nil, false
	}

	data = data[off-aoff:]
	r.MustSeek(int64(length), 1)
	return data, true
}
